using System;
using System.Collections.Generic;
using System.Text;
using ElteIk.RoboSoccer.Core;
using ElteIk.RoboSoccer.Core.Physics;

namespace ElteIk.RoboSoccer.Simulator
{

    #region Base objective class

    /// <summary>
    /// Basic abstract class for an objective.
    /// </summary>
    public abstract class Objective
    {
        protected const int DEFAULTVALUE = 1;
        
        protected int value = DEFAULTVALUE;    // value of the objective
        public int Value { get { return value; } }
        
        protected Boolean isReady;
        virtual public Boolean IsReady() { return isReady; }

        protected String readyText;
        protected String notReadyText;

        protected Objective(int value)
        {
            isReady = false;
            this.value = value;
        }

        virtual public String GetResultText()
        {
            if (IsReady())
            { return readyText; }
            else
            { return notReadyText; }
        }
    }

    #endregion

    #region Team's objectives

    /// <summary>
    /// Handling the team's objectives which should be reach.
    /// </summary>
    public class TeamObjectives
    {
        private RoboSoccerGame game;
        private RoboSoccerTeam team;
        private List<Objective> objectiveList; // list of objectives which should be reach


        public TeamObjectives(RoboSoccerGame game, RoboSoccerTeam team)
        {
            this.game = game;
            this.team = team;
            objectiveList = new List<Objective>();
        }


        public void AddObjective(Objective objective)
        {
            objectiveList.Add(objective);
        }


        public Boolean IsReady()
        {
            foreach (Objective obj in objectiveList)
            {
                if (!obj.IsReady()) { return false; }
            }
            return true;
        }


        public int ReadyObjectivesNumber()
        {
            int i = 0;
            foreach (Objective obj in objectiveList)
            {
                if (obj.IsReady()) { ++i; }
            }
            return i;
        }

        public int ObjectivesNumber() { return objectiveList.Count; }

        public List<Objective> GetReadyObjectives()
        {
            List<Objective> list = new List<Objective>();
            foreach (Objective obj in objectiveList)
            {
                if (obj.IsReady()) { list.Add(obj); }
            }
            return list;
        }

        public List<Objective> GetNotReadyObjectives()
        {
            List<Objective> list = new List<Objective>();
            foreach (Objective obj in objectiveList)
            {
                if (!obj.IsReady()) { list.Add(obj); }
            }
            return list;
        }

        public List<Objective> GetObjectives() { return objectiveList; }

        public String GetTeamResultText()
        {
            String str = "Result of the objectives:\n\n";
            int totalPoint = 0;
            int readyNr = 0;
            foreach (Objective obj in objectiveList)
            {
                str += "-" + obj.GetResultText() + "\n";
                if (obj.IsReady())
                { 
                    totalPoint += obj.Value;
                    readyNr++;
                }
            }
            str += String.Format("\n\n{0}/{1} objectives are ready.\n", readyNr, ObjectivesNumber());
            str += String.Format("The team got {0} points.\n", totalPoint);
            return str;
        }

        public int GetTeamTotalPoint()
        {
            int totalPoint = 0;
            foreach (Objective obj in objectiveList)
            {
                if (obj.IsReady())
                {
                    totalPoint += obj.Value;
                }
            }
            return totalPoint;
        }

    }

    #endregion

    #region Objectives for positioning

    /// <summary>
    /// It will be ready when the physicalObject's center will be closer to targetPosition than maxDistance
    /// </summary>
    public abstract class TargetPositionObjective : Objective
    {
        protected IPhysicalObject physicalObject;
        protected Vector2D targetPosition;
        protected float maxDistance;

        protected TargetPositionObjective(IPhysicalObject physicalObject, Vector2D targetPosition, float maxDistance, int value)
            : base(value)
        {
            this.physicalObject = physicalObject;
            this.targetPosition = targetPosition;
            this.maxDistance = maxDistance;
        }

        override public Boolean IsReady() 
        {
            float d = ((Vector2D)(physicalObject.Position - targetPosition)).Length;
            return (d <= maxDistance);
        } 
    }


    /// <summary>
    /// TargetPosition objective for the ball.
    /// </summary>
    public class BallTargetPositionObjective : TargetPositionObjective
    {
        public BallTargetPositionObjective(Ball ball, Vector2D targetPosition, float maxDistance, int value)
            : base(ball, targetPosition, maxDistance, value) 
        {
            readyText = String.Format("The ball is around it's target position ({0};{1}) R={2}", targetPosition.X, targetPosition.Y, maxDistance);
            notReadyText = String.Format("The ball is NOT around it's target position ({0};{1}) R={2}", targetPosition.X, targetPosition.Y, maxDistance);
        }


        public BallTargetPositionObjective(Ball ball, Vector2D targetPosition, float maxDistance)
            : this(ball, targetPosition, maxDistance, DEFAULTVALUE) { }

    }


    /// <summary>
    /// TargetPosition objective for a robot.
    /// </summary>
    public class PlayerTargetPositionObjective : TargetPositionObjective
    {
        public PlayerTargetPositionObjective(RobotPlayer player, Vector2D targetPosition, float maxDistance, int value)
            : base(player, targetPosition, maxDistance, value) 
        {
            readyText = String.Format("The player Id={0} is around his target position ({1};{2})  R={3}", player.Id, targetPosition.X, targetPosition.Y, maxDistance);
            notReadyText = String.Format("The player Id={0} is NOT around his target position ({1};{2}  R={3})", player.Id, targetPosition.X, targetPosition.Y, maxDistance);
        }

        public PlayerTargetPositionObjective(RobotPlayer player, Vector2D targetPosition, float maxDistance)
            : this(player, targetPosition, maxDistance, DEFAULTVALUE) { }

    }

    #endregion

    #region Objectives for goals


    /// <summary>
    /// Objective for scoring a goal.
    /// </summary>
    public class ScoringGoalObjective : Objective
    {
        private SoccerGame.Teams myTeam;
        
        public ScoringGoalObjective(RoboSoccerGame game, SoccerTeam team, int value)
            :base(value)
        {
            readyText = "The team managed to score a goal.";
            notReadyText = "The team couldn't score a goal.";
            myTeam = (game.LeftTeam == team) ? SoccerGame.Teams.LeftTeam : SoccerGame.Teams.RightTeam;
            game.GoalShoot += new EventHandler<SoccerGame.GoalEventArgs>(GoalScored);
        }

        public ScoringGoalObjective(RoboSoccerGame game, SoccerTeam team)
            : this(game, team, DEFAULTVALUE) { }


        private void GoalScored(object sender, Core.SoccerGame.GoalEventArgs e)
        {
            if (e.ScoringTeam == myTeam)
            {
                isReady = true;
            }
        }
    }

    /// <summary>
    /// Objective for avoiding scoring goal by the opposite team.
    /// </summary>
    public class CleenSheetObjective : Objective
    {
        private SoccerGame.Teams oppositeTeam;
        private Boolean cleanSheet;
        private RoboSoccerGame game;

        public CleenSheetObjective(RoboSoccerGame game, SoccerTeam team, int value)
            :base(value)
        {
            this.game = game;
            cleanSheet = true;
            readyText = "The team didn't get a goal.";
            notReadyText = "The team got a goal.";
            oppositeTeam = (game.LeftTeam == team) ? SoccerGame.Teams.RightTeam : SoccerGame.Teams.LeftTeam;
            game.GoalShoot += new EventHandler<SoccerGame.GoalEventArgs>(GoalScored);
        }

        public CleenSheetObjective(RoboSoccerGame game, SoccerTeam team)
            : this(game, team, DEFAULTVALUE) { }

        private void GoalScored(object sender, Core.SoccerGame.GoalEventArgs e)
        {
            if (e.ScoringTeam == oppositeTeam)
            {
                cleanSheet = false;
            }
        }

        override public Boolean IsReady()
        {
            return (cleanSheet && (game.Time >= game.TimeLimit));
        }
    }

    #endregion
}
